配置路由
1 新建一个项目, 创建一个app01和stark应用,stark创建一个service包,并在service下创建stark.py。然后注册app
2 仿照site.py的注册代码,写stark.py代码:
class ModelStark(object): def __init__(self, model, site): self.model = model self.site = siteclass StarkSite(object): def __init__(self): self._registry = {} def register(self, model, stark_class=None): if not stark_class: stark_class = ModelStark self._registry[model] = stark_class(model, self)site = StarkSite()
3 stark应用下的app.py代码:
from django.apps import AppConfigfrom django.utils.module_loading import autodiscover_modulesclass StarkConfig(AppConfig): name = 'stark' def ready(self): autodiscover_modules('stark')
4 app01 下model.py:
class UserInfo(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.nameclass Book(models.Model): title=models.CharField(max_length=32) def __str__(self): return self.title
5 在app01下stark.py的注册模型:
from stark.service.stark import site, ModelStarkfrom .models import *site.register(Book)site.register(UserInfo)print("_registry", site._registry)
6 在项目的urls.py写路由。
from django.conf.urls import urlfrom django.contrib import adminfrom stark.service.stark import siteurlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^stark/', site.urls),]
7 在service下stark.py写整套urls路由
from django.conf.urls import urlfrom django.shortcuts import HttpResponse,renderclass ModelStark(object): def __init__(self, model, site): self.model = model self.site = siteclass StarkSite(object): def __init__(self): self._registry = {} def register(self, model, stark_class=None): if not stark_class: stark_class = ModelStark self._registry[model] = stark_class(model, self) def add(self, request): return HttpResponse("add") def delete(self, request, id): return HttpResponse("delete") def change(self, request, id): return HttpResponse("change") def list_view(self, request): return HttpResponse("list_view") def get_urls2(self): temp = [] # 添加每个app/model的增删改查url temp.append(url(r'^add/', self.add)) temp.append(url(r'^(\d+)/delete/', self.delete)) temp.append(url(r'^(\d+)/change/', self.change)) temp.append(url(r'^$', self.list_view)) return temp @property def urls2(self): return self.get_urls2(), None, None def get_urls(self): temp = [] for model, stark_class_obj in self._registry.items(): model_name = model._meta.model_name app_label = model._meta.app_label # 添加路由 # url(r'app01/user/',) temp.append(url(r'^%s/%s/' % (app_label, model_name), self.urls2)) return temp @property def urls(self): return self.get_urls(), None, Nonesite = StarkSite()
此时运行项目,就会有stark开头的8条路由。但是每个模型的增删改查的返回数据一样,我们要做到根据不同的app和model返回对应的数据,因此要把增删改查的路由重新划分。
8 在service下stark.py修改urls路由,此时的代码:
from django.conf.urls import urlfrom django.shortcuts import HttpResponse,renderclass ModelStark(object): def __init__(self, model, site): self.model = model self.site = site def add(self, request): return HttpResponse("add") def delete(self, request, id): return HttpResponse("delete") def change(self, request, id): return HttpResponse("change") def list_view(self, request): return HttpResponse("list_view") def get_urls2(self): temp = [] # 添加每个app/model的增删改查url temp.append(url(r'^add/', self.add)) temp.append(url(r'^(\d+)/delete/', self.delete)) temp.append(url(r'^(\d+)/change/', self.change)) temp.append(url(r'^$', self.list_view)) return temp @property def urls2(self): return self.get_urls2(), None, Noneclass StarkSite(object): def __init__(self): self._registry = {} def register(self, model, stark_class=None): if not stark_class: stark_class = ModelStark self._registry[model] = stark_class(model, self) def get_urls(self): temp = [] for model, stark_class_obj in self._registry.items(): model_name = model._meta.model_name app_label = model._meta.app_label # 添加路由 # url(r'app01/user/',) temp.append(url(r'^%s/%s/' % (app_label, model_name), stark_class_obj.urls2)) return temp @property def urls(self): return self.get_urls(), None, Nonesite = StarkSite()
因为每个app和模型类的数据不一样以及各自定制的显示方式不一样,所以对于增删改查就要分开对待,因此就把增删改查放到ModelStark类中,既然四个视图函数都放到ModelStark中了,把调用他们的get_urls2也放进去,这样方便调用,其实就是把self和调用对象保持一致。get_urls2都放进去了,urls2也顺便放进去吧,正好他们是一套。
把他们放到ModelStark的目的就是根据不同的app和model以及他们在注册时定制的配置类显示对应的数据和展示方式。下面的增删改查都会在ModelStark类中进行配置,并且有一个对象会一致被调用:stark_class_obj
假设app01 下stark.py为Book模型定制一个配置类,Userinfo不配置:
class BookConfig(ModelStark): passsite.register(Book, BookConfig)site.register(UserInfo)
此时的路由算是配置好了,后面再设置反向解析,下面开始配置视图。
list_display
首先先看下ModelStark类中的self.model
1 向UserIfo表中,填充一些数据。并写Userinfo配置类:
class UserConfig(ModelStark): list_display = ["name", "age"]
在service/stark.py的ModelStark类中添加代码:
class ModelStark(object): list_display = [] ..... def list_view(self, request): print(self.model) # UserInfo print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.list_display: # 获取每一个要展示的字段 ["name", "age"] val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals()) ........
添加list.html文件,代码:
数据列表
{ % for data_list in new_data_list %}
{ % for data in data_list %} { % endfor %}{ { data }} { % endfor %}
访问/strak/app01/userinfo,此时页面就能显示数据了
2 此时想在每一列的后面放在编辑按钮。
在app01/strak.py中给添加一个方法,使每一条数据都有一个编辑按钮。
from django.utils.safestring import mark_safe ........class UserConfig(ModelStark): def edit(self): user_id = obj.id return mark_safe("编辑") list_display = ["name", "age", edit]......
在service/stark.py的list_view中修改代码:
def list_view(self, request): print(self.model) # UserInfo print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.list_display: # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self) else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals())
此时访问/strak/app01/userinfo
3 此时每一列的后面都有一个编辑连接,但是并不能跳转到对应编辑页面,因此修改url,修改app01/strak.py中的edit方法。
def edit(self, obj): user_id = obj.id return mark_safe("编辑" % user_id)
edit方法需要一个obj参数来获取用户id,在service/stark.py的list_view中给它传递,
def list_view(self, request): print(self.model) # UserInfo print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.list_display: # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals())
此时访问/strak/app01/userinfo,发现每一个编辑按钮都能跳到对应的编辑页面。
4 但是这样写url地址并不是最完美的,然而这样也行,为了更加完美,那就使用反向解析。
修改service/stark.py中get_urls2:
def get_urls2(self): temp = [] # 添加每个app/model的增删改查url model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r'^(\d+)/delete/', self.delete, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r'^(\d+)/change/', self.change, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp
修改app01/strak.py中的edit:
from django.core.urlresolvers import reverseclass UserConfig(ModelStark): def edit(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) return mark_safe("编辑" % _url)
此时再访问/strak/app01/userinfo,发现每一个编辑按钮都能跳到对应的编辑页面。
5 既然编辑都完成了,那就再添加一个删除和checkbox,简直易如反掌。
修改app01/strak.py:
class UserConfig(ModelStark): def edit(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) return mark_safe("编辑" % _url) def deletes(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,)) return mark_safe("删除" % _url) def checkbox(self, obj): return mark_safe("") list_display = [ checkbox, "name", "age", edit, deletes]
此时访问/strak/app01/userinfo,页面效果:
5 但是,如果某个模型类没有定制自己的配置类,也能展示自己的默认字段,并且也有复选框、编辑和删除功能。
把app01/strak.py中的edit、delete、checkbox三个方法全部剪切放到service/stark.py的ModelStark类中,然后把list_display改为list_display = ["__str__"]。为了保证每个模型字段和checkbox、编辑、删除的展示顺序,定义一个new_list_display方法,动态的获取所有的展示字段。具体代码:
class ModelStark(object): list_display = ["__str__"] def __init__(self, model, site): self.model = model self.site = site def edit(self, obj): """编辑按钮""" model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) return mark_safe("编辑" % _url) def deletes(self, obj): """删除按钮""" model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,)) return mark_safe("删除" % _url) def checkbox(self, obj): """复选框""" return mark_safe("") def add(self, request): return HttpResponse("add") def delete(self, request, id): return HttpResponse("delete") def change(self, request, id): return HttpResponse("change") def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp def list_view(self, request): """列表展示页""" print(self.model) # UserInfo print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals()) ..........
此时访问/strak/app01/userinfo和/strak/app01/book,都能展示复选框、默认字段、编辑、删除。
现在表单数据有了,但是表头还没有,那获取表头数据。如果是复选框列,也在表头发一个复选框;如果是编辑或者删除,表头就显示操作;如果是其他就显示字段名称。
6 修改service/stark.py中checkbox、edit、deletes方法,判断获取的是表头还是表单
def edit(self, obj=None, header=False): """编辑按钮""" if header: # 判断是不是表头 return "操作" model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) return mark_safe("编辑" % _url) def deletes(self, obj=None, header=False): """删除按钮""" if header: # 判断是不是表头 return "操作" model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,)) return mark_safe("删除" % _url) def checkbox(self, obj=None, header=False): """复选框""" if header: # 判断是不是表头 return mark_safe("") return mark_safe("")
7 修改service/stark.py中view_list方法,添加获取表头的代码;
# 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self, header=True) head_list.append(val) else: if field == '__str__': val = self.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val)
此时ModelStark类的代码:
class ModelStark(object): list_display = ["__str__"] def __init__(self, model, site): self.model = model self.site = site def edit(self, obj=None, header=False): """编辑按钮""" if header: # 判断是不是表头 return "操作" model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) return mark_safe("编辑" % _url) def deletes(self, obj=None, header=False): """删除按钮""" if header: # 判断是不是表头 return "操作" model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,)) return mark_safe("删除" % _url) def checkbox(self, obj=None, header=False): """复选框""" if header: # 判断是不是表头 return mark_safe("") return mark_safe("") def add(self, request): return HttpResponse("add") def delete(self, request, id): return HttpResponse("delete") def change(self, request, id): return HttpResponse("change") def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp def list_view(self, request): """列表展示页""" print(self.model) # UserInfo print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self, header=True) head_list.append(val) else: if field == '__str__': val = self.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val) # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals()) def get_urls2(self): temp = [] # 添加每个app/model的增删改查url model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r'^(\d+)/delete/', self.delete, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r'^(\d+)/change/', self.change, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls2(self): return self.get_urls2(), None, None
8 修改list.html的代码,并添加复选框的点击事件:
数据列表
{ % for head_name in head_list %} { % for data_list in new_data_list %}{ { head_name }} { % endfor %}{ % for data in data_list %} { % endfor %}{ { data }} { % endfor %}
此时访问/strak/app01/userinfo和/strak/app01/book,表头和表单都有数据了。
list_display_links
首先判断模型类有没有配置list_display_links,如果没有就显示编辑列,如果指定了可点击的字段,那就把这个字段变成可点击的a标签,再把编辑列去掉。
在ModelStart类中,添加类属性list_display_links=[],然后修改new_list_display方法,
1 修改list_viwe中获取表单数据的代码,ModelStart类的部分代码:
class ModelStark(object): list_display = ["__str__"] list_display_links = [] ...... def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) if not self.list_display_links: # 判断是否指定了可点击的列 temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp def list_view(self, request): """列表展示页""" # print(self.model) # UserInfo # print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self, header=True) head_list.append(val) else: if field == '__str__': val = self.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val) # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] # print(ModelStark.list_display_links) for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.list_display_links: # 判断字段是否在list_display_links中, model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) val = mark_safe("%s" % (_url, val)) temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals()) ......
2 在app01/strak.py中修改Userinfo的配置类:
class UserConfig(ModelStark): list_display = ["name", "age"] list_display_links = ["name"]
访问/strak/app01/userinfo,
效果有了,但是下面的代码在很多地方重复使用:
model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,)) val = mark_safe("%s" % (_url, val))
3 这些代码都是在获取url,因此直接封装四个获取url的方法:get_change_url,get_delete_url,get_add_url,get_list_url。
"""获取编辑的url""" def get_change_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url """获取删除的url""" def get_delete_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url """获取添加的url""" def get_add_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url """获取列表的url""" def get_list_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url
4 修改edit、deletes、checkbox的内部代码:
"""编辑按钮""" def edit(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_change_url(obj) return mark_safe("编辑" % _url) """删除按钮""" def deletes(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_delete_url(obj) return mark_safe("删除" % _url) """复选框""" def checkbox(self, obj=None, header=False): if header: # 判断是不是表头 return mark_safe("") return mark_safe("")
5 修改list_view的获取list_display_links的字段的部分代码,list_view的代码:
"""列表展示页""" def list_view(self, request): # print(self.model) # UserInfo # print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self, header=True) head_list.append(val) else: if field == '__str__': val = self.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val) # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] # print(ModelStark.list_display_links) for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.list_display_links: # 判断字段是否在list_display_links中, _url = self.get_change_url(obj) val = mark_safe("%s" % (_url, val)) temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals())
6 同时整理下方法的命名,此时ModelStark类的全部代码:
class ModelStark(object): list_display = ["__str__"] list_display_links = [] def __init__(self, model, site): self.model = model self.site = site """编辑按钮""" def edit(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_change_url(obj) return mark_safe("编辑" % _url) """删除按钮""" def deletes(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_delete_url(obj) return mark_safe("删除" % _url) """复选框""" def checkbox(self, obj=None, header=False): if header: # 判断是不是表头 return mark_safe("") return mark_safe("") """获取编辑的url""" def get_change_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url """获取删除的url""" def get_delete_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url """获取添加的url""" def get_add_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url """获取列表的url""" def get_list_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) if not self.list_display_links: # 判断是否指定了可点击的列 temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp def add_view(self, request): return HttpResponse("add") def delete_view(self, request, id): return HttpResponse("delete") def change_view(self, request, id): return HttpResponse("change") """列表展示页""" def list_view(self, request): # print(self.model) # UserInfo # print(self.list_display) # ["name", "age"] # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self, header=True) head_list.append(val) else: if field == '__str__': val = self.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val) # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] # print(ModelStark.list_display_links) for obj in data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.list_display_links: # 判断字段是否在list_display_links中, _url = self.get_change_url(obj) val = mark_safe("%s" % (_url, val)) temp.append(val) new_data_list.append(temp) return render(request, 'list.html', locals()) def get_urls2(self): temp = [] # 添加每个app/model的增删改查url model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add_view, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r'^(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls2(self): return self.get_urls2(), None, None
增加
现在查的页面已经有了,把增删改的功能也做了。先做增加的。为了表单的复杂,把app01的model.py的模型类都删了,把stark.py里面的注册代码和配置类代码也删了。
1 把下面的模型类代码放到app01的model.py里面,然后执行迁移。
from django.db import models# Create your models here.class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 与AuthorDetail建立一对一的关系 authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) def __str__(self): return self.nameclass AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) def __str__(self): return self.telephoneclass Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() def __str__(self): return self.nameclass Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 authors=models.ManyToManyField(to='Author',) def __str__(self): return self.title
2 app01的stark.py里面的代码:
from stark.service.stark import site, ModelStarkfrom .models import *class BookConfig(ModelStark): list_display = ["title", "price", "publishDate"]site.register(Author)site.register(Publish)site.register(AuthorDetail)site.register(Book,BookConfig)
在list.html里面添加一个跳转到添加数据页面的连接或按钮,并且在list_view里把添加的url传到list.html。
3 使用ModelForm来做表单的处理。ModelStark类中的add_view方法:
def add_view(self, request): class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" form_obj = ModelFormDemo() return render(request, 'add_view.html', locals())
4 添加add_view.html文件,代码:
添加数据
此时访问添加页面,效果
如果想让字段显示中文怎么办。在add_view的ModelFormDemo里面加label? 但是我们并不知道此时访问的是那张数据表,所以不能写死。。那怎么办?让用户自己定义,用户未定义就用默认的。
5 在ModelStark类中定义一个类属性:model_class = None。 定义一个获取用户定义的ModelFormDemo类的方法。然后修改add_view方法。
class ModelStark(object): list_display = ["__str__"] list_display_links = [] model_class = None ......... # 获取定义的ModelFormDemo类 def get_modelform_class(self): if not self.model_class: # 如果用户为定义,返回默认的ModelFormDemo类名 class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo else: # 返回用户定义的ModelFormDemo类名 return self.model_class # 添加视图 def add_view(self, request): ModelFormDemo = self.get_modelform_class() # 取到的是类名 form_obj = ModelFormDemo() return render(request, 'add_view.html', locals()) .......
6 现在去app01下的stark.py中定制一个ModelForm类:
from django.forms import ModelFormclass ModelFormDemo(ModelForm): class Meta: model = Book fields = "__all__" labels = { "title": "书籍名称", "price": "价格" }class BookConfig(ModelStark): list_display = ["title", "price", "publishDate"] model_class = ModelFormDemo .......
此时去页面访问,
OK,现在就做post请求。
7 add_view.py
def add_view(self, request): ModelFormDemo = self.get_modelform_class() # 取到的是类名 if request.method == "POST": form_obj = ModelFormDemo(request.POST) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'add_view.html', locals()) form_obj = ModelFormDemo() return render(request, 'add_view.html', locals())
现在就可以去页面添加数据了。如果进入添加页面时报错没有__str__字段, 在模型类的 __str__方法中将返回值强转str就好了。
ok添加做好了,接下来编辑。
编辑
添加和编辑使用的表单一样,因此两个页面都导入表单的html代码。创建form.html。
1 form.html:
2 add_view.html
添加数据
{ % include 'form.html' %}
3 edit.html
修改数据
{ % include 'form.html' %}
4 ok,页面完成了,写编辑的视图函数。修改change_view方法:
def change_view(self, request, id): ModelFormDemo = self.get_modelform_class() # 取到的是类名 edit_obj = self.model.objects.get(pk=id) if request.method == "POST": form_obj = ModelFormDemo(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'edit_view.html', locals()) form_obj = ModelFormDemo(instance=edit_obj) return render(request, 'edit_view.html', locals())
现在就可以去页面修改数据了。修改做好了,接下来删除。
删除
1 创建delete_view.html
2 修改delete_view方法
def delete_view(self, request, id): list_url = self.get_list_url() if request.method == "POST": self.model.objects.get(pk=id).delete() return redirect(list_url) return render(request, 'delete_view.html', locals())
增删改现在算是大功告成。下面继续查询,因为admin的查询姿势有很多,所以如果继续在list_view方法里写代码会显得比较乱,因此把查询封装在一个方法里面。
3 定义ShowList类:
class ShowList(object): def __init__(self, config, data_list): self.config = config self.data_list = data_list def show_header(self): # 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.config.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self.config, header=True) head_list.append(val) else: if field == '__str__': val = self.config.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.config.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val) return head_list def show_body(self): # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] # print(ModelStark.list_display_links) for obj in self.data_list: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self.config, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.config.list_display_links: # 判断字段是否在list_display_links中, _url = self.config.get_change_url(obj) val = mark_safe("%s" % (_url, val)) temp.append(val) new_data_list.append(temp) return new_data_list
4 修改ModelStark类的list_view,此时ModelStark类的代码:
class ModelStark(object): list_display = ["__str__"] list_display_links = [] model_class = None def __init__(self, model, site): self.model = model self.site = site """编辑按钮""" def edit(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_change_url(obj) return mark_safe("编辑" % _url) """删除按钮""" def deletes(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_delete_url(obj) return mark_safe("删除" % _url) """复选框""" def checkbox(self, obj=None, header=False): if header: # 判断是不是表头 return mark_safe("") return mark_safe("") """获取编辑的url""" def get_change_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url """获取删除的url""" def get_delete_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url """获取添加的url""" def get_add_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url """获取列表的url""" def get_list_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url # 获取被指定的所有字段 def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) if not self.list_display_links: # 判断是否指定了可点击的列 temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp # 获取定义的ModelFormDemo类 def get_modelform_class(self): if not self.model_class: # 如果用户为定义,返回默认的ModelFormDemo类名 class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo else: # 返回用户定义的ModelFormDemo类名 return self.model_class # 添加视图 def add_view(self, request): ModelFormDemo = self.get_modelform_class() # 取到的是类名 if request.method == "POST": form_obj = ModelFormDemo(request.POST) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'add_view.html', locals()) form_obj = ModelFormDemo() return render(request, 'add_view.html', locals()) def delete_view(self, request, id): list_url = self.get_list_url() if request.method == "POST": self.model.objects.get(pk=id).delete() return redirect(list_url) return render(request, 'delete_view.html', locals()) def change_view(self, request, id): ModelFormDemo = self.get_modelform_class() # 取到的是类名 edit_obj = self.model.objects.get(pk=id) if request.method == "POST": form_obj = ModelFormDemo(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'edit_view.html', locals()) form_obj = ModelFormDemo(instance=edit_obj) return render(request, 'edit_view.html', locals()) """列表展示页""" def list_view(self, request): # 获取userinfo 的数据 data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头 show_list = ShowList(self, data_list) head_list = show_list.show_header() # 获取表体 new_data_list = show_list.show_body() # 获取添加的url add_url = self.get_add_url() return render(request, 'list.html', locals()) def get_urls2(self): temp = [] # 添加每个app/model的增删改查url model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add_view, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r'^(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls2(self): return self.get_urls2(), None, None
分页
1 在stark app下创建一个utils包,然后创建一个page.py,代码:
class Pagination(object): def __init__(self, current_page, all_count, base_url,params, per_page_num=8, pager_count=11, ): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param base_url: 分页中显示的URL前缀 :param pager_count: 最多显示的页码个数 """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num self.base_url = base_url # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count # 最多显示页码数 self.pager_count_half = int((pager_count - 1) / 2) import copy params = copy.deepcopy(params) params._mutable = True self.params = params # self.params : {"page":77,"title":"python","nid":1} @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示(11-1)/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_start = self.all_pager - self.pager_count + 1 pager_end = self.all_pager + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] self.params["page"] = 1 first_page = '
2 在ShowList类中生成分页对象和每一页的数据。
class ShowList(object): def __init__(self, config, data_list, request): self.config = config self.data_list = data_list self.request = request # 分页 data_count = self.data_list.count() # 获取数据总数量 current_page = self.request.GET.get("page", 1) # 获取当前页码 base_url = self.request.path # 获取url(不带参数) # 生成分页对象 self.paginator = Pagination(current_page, data_count, base_url, self.request.GET, per_page_num=1, pager_count=11) # 当前页的数据列表 self.page_data = self.data_list[self.paginator.start: self.paginator.end] 。。。。。。。 # list_view传入request
3 然后修改get_body方法中的代码:
4 修改list_view.html,插入页码列表
添加数据
{ % for head_name in head_list %} { % for data_list in new_data_list %}{ { head_name }} { % endfor %}{ % for data in data_list %} { % endfor %}{ { data }} { % endfor %}
Search
1 在ModelStark类中添加类属性search_fields = [],然后添加一个get_search方法,根据search关键字进行模糊查询。
def get_search(self, request): key_word = request.GET.get("q", "") search_connection = Q() if key_word: search_connection.connector = 'or' for field in self.search_fields: search_connection.children.append((field+"__contains", key_word)) return key_word, search_connection
2 修改list_view方法的代码
def list_view(self, request): # 获取search的key_word,Q对象 key_word, search_connection = self.get_search(request) # 获取userinfo 的数据,并进行search过滤 data_list = self.model.objects.all().filter(search_connection) # 获取表头 show_list = ShowList(self, data_list, request) head_list = show_list.show_header() # 获取表体 new_data_list = show_list.show_body() # 获取添加的url add_url = self.get_add_url() return render(request, 'list.html', locals())
3 修改list_view.html
添加数据
{ % for head_name in head_list %} { % for data_list in new_data_list %}{ { head_name }} { % endfor %}{ % for data in data_list %} { % endfor %}{ { data }} { % endfor %}
在app01的stark.py中定义了search_fields后,就能根据设定的字段进行search查询了。
action
1 在ModelStark类中添加类属性actions = [],在ShowList类中定义get_actions_list方法,获取用户定制的所有action操作。
...... class ShowList(object): def __init__(self, config, data_list, request): ........# actions self.actions = self.config.actions # 获取action操作 def get_actions_list(self): temp = [] for action in self.actions: temp.append({ "name": action.__name__, "desc": action.short_description }) return temp.......
2 修改ModelStark类中的checkbox方法:
"""复选框""" def checkbox(self, obj=None, header=False): if header: # 判断是不是表头 return mark_safe("") return mark_safe("" % obj.pk)
3 修改list_view.html,添加下拉框
4 在app01 stark.py中的BookConfig类中定义一个修改价格的action
class BookConfig(ModelStark): list_display = ["title", "price", "publishDate"] model_class = ModelFormDemo search_fields = ["title", "price"] def edit_price_action(self, request, queryset): queryset.update(price=111) edit_price_action.short_description = "修改价格" actions = [edit_price_action]
5 修改ModelStark类中list_view方法。
def list_view(self, request): if request.method == "POST": action_name = request.POST.get("action") # 获取执行的action名称 id_list = request.POST.getlist("selected_id") # 获取被选中的id action_func = getattr(self, action_name) # 反射获取函数 queryset = self.model.objects.filter(pk__in=id_list) # 过滤被选中的查询集 action_func(request, queryset) # 执行action return redirect(self.get_list_url()) 。。。。。。。
ok,现在就能批量的修改书籍价格。
然而admin的action有一个默认的批量删除,so,下面添加这个功能。
1 在ModelStark类中添加一个delete_action方法
def delete_action(self, request, queryset): queryset.delete() delete_action.short_description = "批量删除"
2 然后再定义一个new_action方法
def new_actions(self): temp = [] temp.append(ModelStark.delete_action) temp.extend(self.actions) return temp
3 然后修改ShowList的__init__中self.action
self.actions = self.config.new_actions()
现在就能批量删除了。此时ModelStark类的代码:
class ModelStark(object): list_display = ["__str__"] list_display_links = [] model_class = None search_fields = [] actions = [] filter_fields = [] def __init__(self, model, site): self.model = model self.site = site # 批量删除 def delete_action(self, request, queryset): queryset.delete() delete_action.short_description = "批量删除" """编辑按钮""" def edit(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_change_url(obj) return mark_safe("编辑" % _url) """删除按钮""" def deletes(self, obj=None, header=False): if header: # 判断是不是表头 return "操作" _url = self.get_delete_url(obj) return mark_safe("删除" % _url) """复选框""" def checkbox(self, obj=None, header=False): if header: # 判断是不是表头 return mark_safe("") return mark_safe("" % obj.pk) """获取编辑的url""" def get_change_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url """获取删除的url""" def get_delete_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url """获取添加的url""" def get_add_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url """获取列表的url""" def get_list_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url # 获取所有的action def new_actions(self): temp = [] temp.append(ModelStark.delete_action) temp.extend(self.actions) return temp # 获取被指定的所有字段 def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) if not self.list_display_links: # 判断是否指定了可点击的列 temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp # 获取定义的ModelFormDemo类 def get_modelform_class(self): if not self.model_class: # 如果用户为定义,返回默认的ModelFormDemo类名 class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo else: # 返回用户定义的ModelFormDemo类名 return self.model_class # 添加视图 def add_view(self, request): ModelFormDemo = self.get_modelform_class() # 取到的是类名 if request.method == "POST": form_obj = ModelFormDemo(request.POST) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'add_view.html', locals()) form_obj = ModelFormDemo() print(form_obj) return render(request, 'add_view.html', locals()) # 删除视图 def delete_view(self, request, id): list_url = self.get_list_url() if request.method == "POST": self.model.objects.get(pk=id).delete() return redirect(list_url) return render(request, 'delete_view.html', locals()) # 编辑视图 def change_view(self, request, id): ModelFormDemo = self.get_modelform_class() # 取到的是类名 edit_obj = self.model.objects.get(pk=id) if request.method == "POST": form_obj = ModelFormDemo(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'edit_view.html', locals()) form_obj = ModelFormDemo(instance=edit_obj) return render(request, 'edit_view.html', locals()) # 获取search关键字和search字段 def get_search(self, request): key_word = request.GET.get("q", "") search_connection = Q() if key_word: key_word = key_word.strip() search_connection.connector = 'or' for field in self.search_fields: search_connection.children.append((field+"__contains", key_word)) return key_word, search_connection """列表展示页""" def list_view(self, request): if request.method == "POST": action_name = request.POST.get("action") # 获取执行的action名称 id_list = request.POST.getlist("selected_id") # 获取被选中的id action_func = getattr(self, action_name) # 反射获取函数 queryset = self.model.objects.filter(pk__in=id_list) # 过滤被选中的查询集 action_func(request, queryset) # 执行action return redirect(self.get_list_url()) # 获取search的key_word,Q对象 key_word, search_connection = self.get_search(request) # 获取userinfo 的数据,并进行search过滤 data_list = self.model.objects.all().filter(search_connection) # 获取表头 show_list = ShowList(self, data_list, request) head_list = show_list.show_header() # 获取表体 new_data_list = show_list.show_body() # 获取添加的url add_url = self.get_add_url() return render(request, 'list.html', locals()) def get_urls2(self): temp = [] # 添加每个app/model的增删改查url model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add_view, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r'^(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls2(self): return self.get_urls2(), None, None
ShowList类的代码;
class ShowList(object): def __init__(self, config, data_list, request): self.config = config self.data_list = data_list self.request = request # 分页 data_count = self.data_list.count() # 获取数据总数量 current_page = int(self.request.GET.get("page", 1)) # 获取当前页码 base_url = self.request.path # 获取url(不带参数) # 生成分页对象 self.paginator = Pagination(current_page, data_count, base_url, self.request.GET, per_page_num=2, pager_count=11) # 当前页的数据列表 self.page_data = self.data_list[self.paginator.start: self.paginator.end] # actions self.actions = self.config.new_actions() # 获取action操作 def get_actions_list(self): temp = [] for action in self.actions: temp.append({ "name": action.__name__, "desc": action.short_description }) return temp def show_header(self): # 获取表头信息 # 定义一个列表,格式:["复选框", name , age, "操作"....] head_list = [] for field in self.config.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......] if callable(field): val = field(self.config, header=True) head_list.append(val) else: if field == '__str__': val = self.config.model._meta.model_name.upper() # 返回模型类的名称 else: val = self.config.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称 head_list.append(val) return head_list def show_body(self): # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in self.page_data: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self.config, obj) # 给自定义方法传递参数 else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.config.list_display_links: # 判断字段是否在list_display_links中, _url = self.config.get_change_url(obj) val = mark_safe("%s" % (_url, val)) temp.append(val) new_data_list.append(temp) return new_data_list
filter_fields
1 在ModelStark类中定义一个类属性filter_fields=[],然后再ShowList类中定义一个get_filter_linktags方法,代码:
def get_filter_linktags(self): link_dict = {} # 定义字段对应的a连接 {"book":["金平..", ""], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ] url_params = copy.deepcopy(self.request.GET) # 获取参数 current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象 data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...] temp = [] # 定义一个临时列表 for obj in data_list: url_params[filter_field] = obj.pk _url = url_params.urlencode() if int(current_field_id) == obj.pk: link_tag = "%s" % (_url, str(obj)) else: link_tag = "%s" % (_url, str(obj)) temp.append(link_tag) link_dict[filter_field] = temp return link_dict
2 app01 stark.py的BookConfig类中定义过滤字段
class BookConfig(ModelStark): list_display = ["title", "price", "publishDate"] model_class = ModelFormDemo search_fields = ["title", "price"] def edit_price_action(self, request, queryset): queryset.update(price=111) edit_price_action.short_description = "修改价格" actions = [edit_price_action] filter_fields = ["authors", "publish"] # 先用多对多和一对多的字段
3 修改list_view.html代码,加一个过滤布局并传数据:
Filter
{ % for field, link_tag in show_list.get_filter_linktags.items %}{ % endfor %}{ { field|upper }}
{ % for tag in link_tag %}{ { tag|safe }}
{ % endfor %}
此时就能显示要过滤的所有字段和对应的数据,而且连接也拼接无误。现在改下a标签的样式。
4 修改get_filter_linktags方法:
def get_filter_linktags(self): link_dict = {} # 定义字段对应的a连接 {"book":["金平..", ""], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ] url_params = copy.deepcopy(self.request.GET) # 获取参数 current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象 data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...] temp = [] # 定义一个临时列表 for obj in data_list: url_params[filter_field] = obj.pk _url = url_params.urlencode() if int(current_field_id) == obj.pk: link_tag = "%s" % (_url, str(obj)) else: link_tag = "%s" % (_url, str(obj)) temp.append(link_tag) link_dict[filter_field] = temp return link_dict
5 给list_view.html中的a标签加样式
6 现在给每个过滤的字段都加一个all标签。
修改get_filter_linktags方法
def get_filter_linktags(self): link_dict = {} # 定义字段对应的a连接 {"book":["金平..", ""], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ] url_params = copy.deepcopy(self.request.GET) # 获取参数 current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象 data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...] temp = [] # 定义一个临时列表 # all标签 if url_params.get(filter_field): # if GET请求参数中包含当前循环的字段,就把这个参数(字段)删除 del url_params[filter_field] temp.append("ALL" % url_params.urlencode()) else: # 不存在就说明该字段没有被选中 temp.append("ALL") # 数据标签 for obj in data_list: url_params[filter_field] = obj.pk _url = url_params.urlencode() if int(current_field_id) == obj.pk: link_tag = "%s" % (_url, str(obj)) else: link_tag = "%s" % (_url, str(obj)) temp.append(link_tag) link_dict[filter_field] = temp return link_dict
OK 样式有了,url也有了,进行数据过滤。
7 在ModelStark类中添加get_filter_data方法,并修改list_view方法:
# 过滤数据的查询条件 def get_filter_data(self, request): filter_condition = Q() for field, pk in request.GET.items(): if field in self.filter_fields: filter_condition.children.append((field, pk)) return filter_condition """列表展示页""" def list_view(self, request): if request.method == "POST": action_name = request.POST.get("action") # 获取执行的action名称 id_list = request.POST.getlist("selected_id") # 获取被选中的id action_func = getattr(self, action_name) # 反射获取函数 queryset = self.model.objects.filter(pk__in=id_list) # 过滤被选中的查询集 action_func(request, queryset) # 执行action return redirect(self.get_list_url()) # 获取search的key_word,Q对象 key_word, search_connection = self.get_search(request) # 过滤 filter_connection = self.get_filter_data(request) # 获取userinfo 的数据,并进行search过滤 data_list = self.model.objects.all().filter(search_connection).filter(filter_connection) # 获取表头 show_list = ShowList(self, data_list, request) head_list = show_list.show_header() # 获取表体 new_data_list = show_list.show_body() # 获取添加的url add_url = self.get_add_url() return render(request, 'list.html', locals())
8 现在就能进行正常的过滤了,只不过现在的能过滤的字段只能是一对多或者多对多。下面处理普通字段的过滤。
修改get_filter_linktags方法
def get_filter_linktags(self): link_dict = {} # 定义字段对应的a连接 {"book":["金平..", ""], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ] url_params = copy.deepcopy(self.request.GET) # 获取参数 current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象 if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): # 如果字段对象是一对多或者多对多 data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...] else: data_list = self.config.model.objects.all().values("pk", filter_field) # 取普通字段的pk和该字段的所有数据 temp = [] # 定义一个临时列表 # all标签 if url_params.get(filter_field): # if GET请求参数中包含当前循环的字段,就把这个参数(字段)删除 del url_params[filter_field] temp.append("ALL" % url_params.urlencode()) else: # 不存在就说明该字段没有被选中 temp.append("ALL") # 数据标签 for obj in data_list: # 继续判断,如果是一对多或者多对多,就用对象去获取pk和值 if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): pk = obj.pk text = str(obj) url_params[filter_field] = pk # 字段作为键,pk作为值 ?publish=1&authors=2 else: pk = obj.get("pk") text = obj.get(filter_field) url_params[filter_field] = text # 字段作为键,实际数据作为值 ?title="金平没" _url = url_params.urlencode() if current_field_id == str(pk) or current_field_id == text: link_tag = "%s" % (_url, text) else: link_tag = "%s" % (_url, text) temp.append(link_tag) link_dict[filter_field] = temp return link_dict
现在在app01 stark.py中的BookConfig类中filter_fields添加"title"字段
class BookConfig(ModelStark): list_display = ["title", "price", "publishDate"] model_class = ModelFormDemo search_fields = ["title", "price"] def edit_price_action(self, request, queryset): queryset.update(price=111) edit_price_action.short_description = "修改价格" actions = [edit_price_action] filter_fields = ["title", "authors", "publish"]
此时页面上的filter中也能显示title字段的所有值,并且也能完成过滤。
9 然而现在页面上不能显示多对多的字段数据,因为多对多的字段有不止一个值,所以页面的显示效果可能会乱,下面做个简单处理。。
修改ShowList中show_body方法。
def show_body(self): # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in self.page_data: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self.config, obj) # 给自定义方法传递参数 else: field_obj = self.config.model._meta.get_field(field) if isinstance(field_obj, ManyToManyField): vals = getattr(obj, field).all() # 获取所有数据 new_temp = [] for i in vals: new_temp.append(str(i)) val = ",".join(new_temp) else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.config.list_display_links: # 判断字段是否在list_display_links中, _url = self.config.get_change_url(obj) val = mark_safe("%s" % (_url, val)) temp.append(val) new_data_list.append(temp) return new_data_list
在app01 stark.py中的BookConfig类中list_display添加"authors"字段
此时访问/stark/app01/book/时, 就能显示authors这一列了。但是如果我们访问其他model的列表页时可能会报错,假如访问stark/app01/author/,然后就会有这样的提示
这是因为(以author表为例):如果用户没有给author配置list_display,那么就会使用默认的__str__,但是当程序走到show_body的这里时,
查不到__str__的字段对象,因此会报错。解决办法,异常捕获。
修改show_body方法,show_body代码:
def show_body(self): # 获取表单信息 # 定义一个新的数据列表 格式: """ [ ["name", "age"] ["name", "age"] ....... ] """ new_data_list = [] for obj in self.page_data: # 获取data_list中的每一个对象 temp = [] # 定义一个内层列表,存储一个对象所有字段的值 for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"] if callable(field): # 判断字段是否可被调用 val = field(self.config, obj) # 给自定义方法传递参数 else: try: field_obj = self.config.model._meta.get_field(field) if isinstance(field_obj, ManyToManyField): vals = getattr(obj, field).all() # 获取所有数据 new_temp = [] for i in vals: new_temp.append(str(i)) val = ",".join(new_temp) else: val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值, if field in self.config.list_display_links: # 判断字段是否在list_display_links中, _url = self.config.get_change_url(obj) val = mark_safe("%s" % (_url, val)) except Exception as e: val = getattr(obj, field) temp.append(val) new_data_list.append(temp) return new_data_list
pop
当我们在admin添加数据的时候,如果哪个字段和其他表有关联,可以在输入框的后面点击加号去添加关联表的数据。下面做这个功能。
1 修改添加页面的样式,修改add_view.html,因为add_view.html使用form.html,因此在form.html上修改。
此时页面上每个表单的后面都有加号按钮,但是一些和其他表没有关联的字段是不应该有加号的,因此应该在后台进行判断。因为使用的是ModelForm组件,因此判断字段的类型是不是ModelChoiceField即可。
2 修改ModelStark类中的add_view
def add_view(self, request): ModelFormDemo = self.get_modelform_class() # 取到的是类名 form_obj = ModelFormDemo() for bfield in form_obj: if isinstance(bfield.field, ModelChoiceField): # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串; bfield.is_related = True if request.method == "POST": form_obj = ModelFormDemo(request.POST) if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) return render(request, 'add_view.html', locals())
这时候只有publish和author后面有加号。
3 现在是做点击事件,修改form.html,给a标签添加一个click事件,让它跳转到对应的添加页面,因此还需要一个url。
先去add_view把url获取了
def add_view(self, request): ModelFormDemo = self.get_modelform_class() # 取到的是类名 form_obj = ModelFormDemo() for bfield in form_obj: if isinstance(bfield.field, ModelChoiceField): # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串; bfield.is_related = True # 获取该字段的模型表和模型表的app # bfield.field.queryset.model 一对多或者多对多字段的关联模型表 relateed_model_name = bfield.field.queryset.model._meta.model_name relateed_app_label = bfield.field.queryset.model._meta.app_label _url = reverse("%s_%s_add" % (relateed_app_label, relateed_model_name)) bfield.add_url = _url
4 修改form.html,给a标签添加一个click事件
{ % endfor %}